package rocky.portlet;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Map;

import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.GenericPortlet;
import javax.portlet.PortletConfig;
import javax.portlet.PortletException;
import javax.portlet.PortletRequestDispatcher;
import javax.portlet.PortletSession;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;

import openones.corewa.config.ConfigUtil;
import openones.corewa.config.CoreWa;
import openones.corewa.config.Event;
import openones.corewa.config.Screen;

import org.apache.log4j.Logger;

import rocky.common.CommonUtil;
import rocky.mvcportlet.control.BaseControl;

/**
 * MVCPortlet Portlet Class.
 * 
 * @author Thach Le
 */
public class MVCPortlet extends GenericPortlet {
    static final Logger LOG = Logger.getLogger("MVCPortlet");
    static final String KEY_CFG = "mvcportlet-config";
    /** Value of KEY_CTXPATH will replaced renderRequest.getContextPath(). */
    static final String KEY_CTXPATH = "contextPath";

    private final static String DEF_PROCID = "init";
    private static final String DEF_SCOPE = "request";

    CoreWa coreWebAppCfg = null;

    String nextScrId = null;
    BaseOutForm resultForm = null;

    /** Configurable context path which is used for locate the script resource. */
    String contextPath;

    /** In case the portlet is loaded in the first time, the method "init" of the controller of view screen is invoked. */
    boolean viewFirstLoad = true;
    private String formBeanName = null;
    private String formBeanScope = null;

    @Override
    public void init(PortletConfig portletCfg) throws PortletException {
        super.init(portletCfg);
        String mvcPortletCfg = portletCfg.getInitParameter(KEY_CFG);
        contextPath = portletCfg.getInitParameter(KEY_CTXPATH);
        LOG.debug("Load configuration file '" + mvcPortletCfg + "'");
        String realPathCfg = portletCfg.getPortletContext().getRealPath(mvcPortletCfg);
        LOG.debug("Load real path configuration file '" + realPathCfg + "'");

        // Parse the configuration file
        coreWebAppCfg = ConfigUtil.parse(realPathCfg);

        // Debug
        LOG.debug("Number of screens:" + coreWebAppCfg.getScreens().size());
    }

    /**
     * The portlet container invokes this doView after method "processAction" finished.
     */
    public void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException {
        PortletSession session;
        // response.setContentType("text/html");
        String displayScreen;

        try {
            if ((nextScrId == null) || (nextScrId.length() == 0)) {
                String viewScreenId = coreWebAppCfg.getViewScreen();
                Screen viewScreen = coreWebAppCfg.getScreen(viewScreenId);
                
                if (viewScreen == null) {
                    LOG.error("View screen '" + viewScreenId + "'" + " not found.");
                }
                
                displayScreen = viewScreen.getInputPage();

                if (viewFirstLoad && (CommonUtil.isNNandNB(viewScreen.getCtrlClass()))) {
                    LOG.debug("The Portlet is viewed in the first time...");
                    try {
                        // Invoke default method "init"
                        Method method;

                        LOG.info("doView.Invoke method '" + DEF_PROCID + "' of class '" + viewScreen.getCtrlClass()
                                + "'");
                        Object controlClass = Class.forName(viewScreen.getCtrlClass()).newInstance();
                        method = controlClass.getClass().getMethod(DEF_PROCID, RenderRequest.class,
                                RenderResponse.class);

                        // Support old version
                        // Object result = method.invoke(controlClass, request, response);
                        resultForm = (BaseOutForm) method.invoke(controlClass, request, response);

                        LOG.debug("Result data:" + resultForm);
                    } catch (Exception ex) {
                        LOG.error("", ex);
                    }
                }
            } else {

                displayScreen = nextScrId;
            }
            LOG.debug("displayScreen=" + displayScreen);

            // Prepare data
            if (CommonUtil.isNNandNB(formBeanName)) {
                if ((formBeanScope == null) || (formBeanScope.length() == 0)) {
                    formBeanName = DEF_SCOPE;
                }
                if ("request".equals(formBeanScope)) {
                    request.setAttribute(formBeanName, resultForm.getInForm());
                } else if ("session".equals(formBeanScope)) {
                    request.getPortletSession().setAttribute(formBeanName, resultForm.getInForm());
                }
            }

            // Prepare key contextPath with value from portlet.xml
            request.setAttribute("contextPath", contextPath);

            //
            if (resultForm != null) {
                // Scan object in session map to put into the session of portlet
                Map<String, Object> sessionMap = resultForm.getSessionMap();
                session = request.getPortletSession();
                if (sessionMap.keySet() != null) {
                    for (String key : sessionMap.keySet()) {
                        LOG.debug("Set request attribute: key = " + key);
                        session.setAttribute(key, sessionMap.get(key));
                    }
                }

                // Scan object in reqest map to put into the request of portlet
                Map<String, Object> requestMap = resultForm.getRequestMap();
                if (requestMap.keySet() != null) {
                    for (String key : requestMap.keySet()) {
                        LOG.debug("Set request attribute: key = " + key);
                        request.setAttribute(key, requestMap.get(key));
                    }
                }
            }
            PortletRequestDispatcher dispatcher = getPortletContext().getRequestDispatcher(displayScreen);
            dispatcher.include(request, response);
        } catch (Throwable th) {
            LOG.debug("Could not execute View model portlet.", th);
        }

    }

    public void doEdit(RenderRequest request, RenderResponse response) throws PortletException, IOException {

        // response.setContentType("text/html");
        //
        // PortletRequestDispatcher dispatcher = getPortletContext().getRequestDispatcher(
        // "/WEB-INF/jsp/MVCPortlet_edit.jsp");
        // dispatcher.include(request, response);

    }

    public void doHelp(RenderRequest request, RenderResponse response) throws PortletException, IOException {

        // response.setContentType("text/html");
        //
        // PortletRequestDispatcher dispatcher = getPortletContext().getRequestDispatcher(
        // "/WEB-INF/jsp/MVCPortlet_help.jsp");
        // dispatcher.include(request, response);

    }

    public void processAction(ActionRequest request, ActionResponse response) throws PortletException, IOException {
        Screen screen;
        Event event;
        Object controlClass;
        String procId = null;
        boolean isRedirect = false;

        LOG.debug("processAction.START");

        // ScreenForm screenForm = (ScreenForm) BaseControl.getData(request, ScreenForm.class);
        Map<String, Object> mapReq = BaseControl.getMapData(request);

        // Get screen id and event id
        String screenId = (String) mapReq.get("screenId");
        String eventId = (String) mapReq.get("eventId");

        LOG.debug("screenId=" + screenId + ";eventId=" + eventId);

        screen = coreWebAppCfg.getScreen(screenId);
        event = screen.getEvent(eventId);

        if (event != null) {
            procId = event.getProcId();
            nextScrId = event.getNextScrId();
            isRedirect = event.isRedirect();
            formBeanName = event.getFormBean();
            formBeanScope = event.getScope();
        } else {
            LOG.warn("Could not lookup the event id '" + eventId + "'");
        }

        if ((procId == null) || (procId.length() == 0)) { // procId is not declared, it is default
            procId = DEF_PROCID;
        }

        if ((nextScrId == null) || (nextScrId.length() == 0)) { // the next screen id is not declared, it is
            // the same.
            nextScrId = screen.getInputPage();
        }

        LOG.debug("procId=" + procId + ";nextScreenId=" + nextScrId + ";redirect=" + isRedirect);

        if (CommonUtil.isNNandNB(screen.getCtrlClass()) && (!isRedirect)) {
            try {
                Method method;

                controlClass = Class.forName(screen.getCtrlClass()).newInstance();
                LOG.info("processAction.Invoke method '" + procId + "' of class '" + screen.getCtrlClass() + "'");
                try {
                    method = controlClass.getClass().getMethod(procId, ActionRequest.class, Map.class,
                            ActionResponse.class);
                    resultForm = (BaseOutForm) method.invoke(controlClass, request, mapReq, response);
                } catch (Exception e) {
                    LOG.warn("Could not invoke method xxx(ActionRequest, Map, ActionRespone)", e);
                    // @deprecated
                    method = controlClass.getClass().getMethod(procId, ActionRequest.class, ActionResponse.class);
                    resultForm = new BaseOutForm();

                    resultForm.setInForm((BaseInForm) method.invoke(controlClass, request, response));
                }

                // resultForm = (BaseForm) method.invoke(controlClass, request, response);
                LOG.debug("Result data:" + resultForm);

            } catch (Exception ex) {
                LOG.error("", ex);
            }
        } else if (isRedirect && (!CommonUtil.isNNandNB(screen.getCtrlClass()))) {
            LOG.warn("The event is not redirect but no Controller!!!");
        }

        viewFirstLoad = false;

        LOG.debug("processAction.END");
    }

}
